#include "sfFlashControl.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Ole2.h>
#include <OleIdl.h>
#include <OCIdl.h>
#import "PROGID:ShockwaveFlash.ShockwaveFlash" named_guids

namespace sf
{
	FlashControl::FlashControl(int width, int height)
		:SwfUI::Control(width,height),dirty(false)
	{
		image.Create(width,height,sf::Color(0,0,0,0));
		//set up sub image buffer for texture updating
		subImage = new unsigned char[width*height*4];
		memset(subImage,0xffffffff,width*height*4);
		//set up drawing buffer for flash
		HDC dc2 = GetDC(NULL);
		dc= CreateCompatibleDC(dc2);		
		ReleaseDC(NULL,dc2);
		BITMAPINFOHEADER bih = {0};
		bih.biSize = sizeof(BITMAPINFOHEADER);
		bih.biBitCount = 32;
		bih.biCompression = BI_RGB;
		bih.biPlanes = 1;
		bih.biWidth = width;
		bih.biHeight = height;
		bitmap = CreateDIBSection((HDC)dc,(BITMAPINFO*)&bih, DIB_RGB_COLORS, (void**)&dib, 0, 0);
		memset(dib,0,width*height*4);
		old=(HBITMAP)SelectObject((HDC)dc,(HBITMAP)bitmap);
		//the OLE object for drawing
		flashObject->QueryInterface(&view);
		//set up sprite
		SetImage(image);
		SetSubRect(sf::IntRect(0,0,width,height));
		FlipY(true);
		invalidateRect(0,0,width,height);
	}

	void FlashControl::resetDirty() const
	{
		dirty = false;
		dleft=width;
		dright=0;
		dtop=height;
		dbottom=0;
	}

	FlashControl::~FlashControl()
	{		
		view->Release();
		SelectObject((HDC)dc,(HBITMAP)old);		
		DeleteObject(bitmap);
		DeleteDC((HDC)dc);
		delete[] subImage;
	}

	void FlashControl::invalidateRect(int left, int top, int right, int bottom)
	{
#ifndef min
#	define min(a,b) (a) < (b) ? (a) : (b)
#endif

#ifndef max
#	define max(a,b) (a) < (b) ? (b) : (a)
#endif
		dirty = true;
		dleft = min(dleft,left);
		dright = max(dright,right);
		dtop = min(dtop,top);
		dbottom = max(dbottom,bottom);

		dleft = max(0,dleft);
		dright = min(width,dright);
		dtop = max(0,dtop);
		dbottom = min(height,dbottom);

#undef min
#undef max
	}

	void FlashControl::Render(sf::RenderTarget& Target, sf::Renderer& Renderer) const
	{
		if(dirty)
		{
			//calculate dirty rectangle width and height
			int dirtyWidth = dright - dleft;
			int dirtyHeight = dbottom - dtop;
			//calculate drawing rectangle
			RECT local;
			local.left = -dleft; //we draw the dirty area on the top left corner of the buffer
			local.top = -dtop;
			local.right = local.left + dright;
			local.bottom = local.top + dbottom;
			
			memset(dib + (height-dirtyHeight) * width * 4,0,width*dirtyHeight*4);//erase the top left corner before drawing
			if(transparent)
			{
				OleDraw(view,DVASPECT_TRANSPARENT,(HDC)dc,&local);
			}
			else
			{
				OleDraw(view,DVASPECT_OPAQUE,(HDC)dc,&local);
			}

			//image.Bind();//reflect changes in the texture
			for(int i = 0; i < dirtyHeight; ++i)
			{
				memcpy(subImage + 4*dirtyWidth*i,dib + (height-dirtyHeight) * width * 4 + i * width * 4, dirtyWidth*4);
			}
			//glTexSubImage2D(GL_TEXTURE_2D,0,dleft,height-dbottom,dirtyWidth,dirtyHeight,GL_RGBA,GL_UNSIGNED_BYTE,subImage);
			image.UpdatePixels((sf::Uint8*)subImage,sf::IntRect(dleft,height-dbottom,dirtyWidth,dirtyHeight));

			resetDirty();
		}

		sf::Sprite::Render(Target,Renderer);
	}

	void FlashControl::transformToLocal(int& x, int& y) const
	{
		sf::Vector2f local = TransformToLocal(sf::Vector2f((float)x,(float)y));
		x = (int)local.x;
		y = (int)local.y;
	}

	void FlashControl::updateAll()
	{//SFML 2.0 does not process non-SFML windows' messages anymore
		MSG Message;
		while (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&Message);
			DispatchMessage(&Message);
		}
	}
}